<template>
  <tree
    v-model="tagIds"
    :multiple="true"
    :flat="flat"
    :default-expand-level="1"
    :options="serializedTagList"
    :placeholder="$t('select_tag')"
    :no-options-text="$t('No_options_select')"
    :limit-text="n => `${$t('Selected_xx_hashtag').replace('xx', n)}`"
    :value-consists-of="valueConsistOf"
  />
</template>
<script>
import Tree from '@riophae/vue-treeselect';
import '@riophae/vue-treeselect/dist/vue-treeselect.css';
import { getALLTags } from '@/api/preview';
import { LockFunction } from '@/utils/structure';

const LANGUAGE = { ZH: 'zh-Hans', KM: 'km-KH' };
const LANGUAGES = [LANGUAGE.ZH, LANGUAGE.KM];

const wrap = t => (Array.isArray(t) ? t : [t]);
const getTagList = LockFunction(getALLTags);

function convertTreeToMap(tree, fn) {
  const convert = (acc, item) => {
    if (item) {
      const nextAcc = { ...acc, ...fn(item) };
      if (Array.isArray(item.children)) {
        return item.children.reduce((o, t) => convert(o, t), nextAcc);
      }
      return nextAcc;
    }
    return acc;
  };
  return convert({}, tree);
}

function deepPick(list, fn) {
  const pick = l => {
    if (Array.isArray(l)) {
      if (!l.length) return null;
      const t = l.pop();
      if (fn(t)) return t;
      return pick(l, fn);
    }
    if (fn(l)) return l;
    return null;
  };
  return pick([...list], fn);
}

export default {
  components: { Tree },

  props: {
    value: {
      type: Array,
      default: () => []
    },
    // https://vue-treeselect.js.org/#flat-mode-and-sort-values
    flat: {
      type: Boolean,
      default: false
    },
    valueConsistOf: {
      type: String,
      default: 'LEAF_PRIORITY',
      validator(value) {
        // https://vue-treeselect.js.org/#prevent-value-combining
        const values = [
          'All',
          'BRANCH_PRIORITY',
          'LEAF_PRIORITY',
          'ALL_WITH_INDETERMINATE'
        ];
        return values.some(t => t === value);
      }
    },
    language: {
      type: [String, Array],
      default: () => [...LANGUAGES],
      validator(value) {
        if (Array.isArray(value)) {
          return value.every(t => LANGUAGES.some(m => m === t));
        }
        if (typeof value === 'string') {
          return LANGUAGES.some(t => t === value);
        }
        return false;
      }
    },
    valueType: {
      type: String,
      default: 'id', // id, object
      validator(value) {
        return ['id', 'object'].some(k => k === value);
      }
    },
    valueShape: {
      type: Object,
      default: () => ({
        key: 'tagName',
        value: 'tagId'
      }),
      validator(t) {
        const isString = s => typeof s === 'string';
        return isString(t.key) && isString(t.value);
      }
    }
  },

  data() {
    return {
      tagList: []
    };
  },

  computed: {
    shapeKey() {
      return this.valueShape.key;
    },
    shapeValue() {
      return this.valueShape.value;
    },
    serializedTagList() {
      const root = [
        { prop: LANGUAGE.ZH, name: this.$t('zh_hans'), id: -1 },
        { prop: LANGUAGE.KM, name: this.$t('khmer'), id: -2 }
      ];
      const getId = this.$pick('id');
      const list = wrap(this.language);
      const tree = root
        .filter(t => list.some(m => m === t.prop))
        .map(t => deepPick(this.tagList, m => getId(m) === getId(t)))
        .filter(t => t);
      return tree.length > 1 ? tree : this.$pick('0.children')(tree) || [];
    },
    tagIds: {
      get() {
        return this.deserializeValue(this.value);
      },
      set(value) {
        this.$emit('input', this.serializeValue(value));
      }
    }
  },

  created() {
    this.fetchTagList();
  },

  methods: {
    getTagList() {
      return this.serializedTagList;
    },
    serializeValue(value) {
      switch (this.valueType) {
        case 'object': {
          const getShape = this.$pick({
            [this.shapeKey]: 'label',
            [this.shapeValue]: 'id'
          });
          return value.map(id => getShape(this.$tagMap[id]));
        }
        default:
          return value;
      }
    },

    deserializeValue(value) {
      switch (this.valueType) {
        case 'object': {
          return value[this.shapeValue];
        }
        default:
          return value;
      }
    },

    serializeTree(tree) {
      const serialize = t => {
        if (!t) return t;
        if (Array.isArray(t)) return t.map(serialize);
        const item = this.$pick({ label: 'tagName', id: 'id' })(t);
        if (t.children) item.children = t.children.map(serialize);
        return item;
      };
      return serialize(tree);
    },

    async fetchTagList() {
      const res = await getTagList();
      if (res.state === 1) {
        this.tagList = this.serializeTree(res.result || []);
        this.$tagMap = this.tagList.reduce(
          (o, t) => ({
            ...o,
            ...convertTreeToMap(t, item => ({
              [item.id]: this.$pick(['label', 'id'])(item)
            }))
          }),
          {}
        );
      }
    }
  }
};
</script>

<style lang="scss" scoped>
.vue-treeselect {
  width: 225px;
  display: inline-block;
  line-height: 17px;
}

/deep/ .vue-treeselect__control {
  padding: 0;
  height: 40px;
  box-sizing: border-box;
  border: 1px solid #dcdfe6;
  border-radius: 4px;
}

/deep/ .vue-treeselect__value-container {
  height: 39px;
  line-height: 17px;
  padding: 0 15px;
}

/deep/ .vue-treeselect__placeholder {
  padding: 0 15px;
  color: #bfc3cb;
  font-size: 14px;
  height: 40px;
  line-height: 40px;
}

/deep/ .vue-treeselect__input-container {
  padding: 0 !important;
}

/deep/ .vue-treeselect__multi-value {
  margin: 0 !important;
}

/deep/ .vue-treeselect__limit-tip {
  padding: 0 !important;
}

/deep/ .vue-treeselect__limit-tip-text {
  margin: 0 !important;
  padding: 0 !important;
  color: #606266;
  font-size: 14px;
  height: 39px;
  line-height: 17px;
}

/deep/ .vue-treeselect__input {
  padding: 0 !important;
}
</style>
